package com.indextank.roo.addon;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.springframework.roo.addon.web.mvc.controller.scaffold.WebScaffoldAnnotationValues;
import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.BooleanAttributeValue;
import org.springframework.roo.classpath.details.annotations.StringAttributeValue;
import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.Path;
import org.springframework.roo.support.style.ToStringCreator;
import org.springframework.roo.support.util.Assert;

/**
 * Metadata for {@link RooIndextankWebSearchable}.
 */
public class IndextankWebSearchMetadata extends AbstractItdTypeDetailsProvidingMetadataItem {
	private static final String PROVIDES_TYPE_STRING = IndextankWebSearchMetadata.class.getName(); 
	private static final String PROVIDES_TYPE = MetadataIdentificationUtils.create(PROVIDES_TYPE_STRING);

	public IndextankWebSearchMetadata(String identifier, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata, IndextankWebSearchAnnotationValues annotationValues, WebScaffoldAnnotationValues webScaffoldAnnotationValues, IndextankSearchAnnotationValues indextankSearchAnnotationValues) {
		super(identifier, aspectName, governorPhysicalTypeMetadata);
		Assert.notNull(webScaffoldAnnotationValues, "Web scaffold annotation values required");
		Assert.notNull(annotationValues, "Indextank web searchable annotation values required");
		Assert.notNull(indextankSearchAnnotationValues, "Indextank search annotation values required");
		Assert.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid");

		if (!isValid()) {
			return;
		}
	
	
		if (annotationValues.getSearchMethod() != null && annotationValues.getSearchMethod().length() > 0) {
//			builder.addMethod(getSearchMethod(annotationValues, indextankSearchAnnotationValues, webScaffoldAnnotationValues));
		}
		
		// Create a representation of the desired output ITD
		itdTypeDetails = builder.build();
	}
	
/*
	private MethodMetadata getSearchMethod(IndextankWebSearchAnnotationValues indextankWebSearchAnnotationValues, IndextankSearchAnnotationValues searchAnnotationValues, WebScaffoldAnnotationValues webScaffoldAnnotationValues) {
		JavaType targetObject = webScaffoldAnnotationValues.getFormBackingObject();
		Assert.notNull(targetObject, "Could not aquire form backing object for the '" + webScaffoldAnnotationValues.getGovernorTypeDetails().getName().getFullyQualifiedTypeName() + "' controller");
		
		JavaSymbolName methodName = new JavaSymbolName(indextankWebSearchAnnotationValues.getSearchMethod());
		MethodMetadata method = methodExists(methodName);
		if (method != null) return method;

		List<AnnotatedJavaType> paramTypes = new ArrayList<AnnotatedJavaType>();
		List<JavaSymbolName> paramNames = new ArrayList<JavaSymbolName>();
		
		paramTypes.add(new AnnotatedJavaType(new JavaType("String"), getRequestParamAnnotation("q", false)));
		paramNames.add(new JavaSymbolName("q"));
		
		paramTypes.add(new AnnotatedJavaType(new JavaType("String"), getRequestParamAnnotation("fq", false)));
		paramNames.add(new JavaSymbolName("facetQuery"));
		
		paramTypes.add(new AnnotatedJavaType(new JavaType("Integer"), getRequestParamAnnotation("page", false)));
		paramNames.add(new JavaSymbolName("page"));
		
		paramTypes.add(new AnnotatedJavaType(new JavaType("Integer"), getRequestParamAnnotation("size", false)));
		paramNames.add(new JavaSymbolName("size"));
		
		paramTypes.add(new AnnotatedJavaType(new JavaType("org.springframework.ui.ModelMap"), null));	
		paramNames.add(new JavaSymbolName("modelMap"));
		
		List<AnnotationAttributeValue<?>> requestMappingAttributes = new ArrayList<AnnotationAttributeValue<?>>();
		requestMappingAttributes.add(new StringAttributeValue(new JavaSymbolName("params"), "search"));
		AnnotationMetadataBuilder requestMapping = new AnnotationMetadataBuilder(new JavaType("org.springframework.web.bind.annotation.RequestMapping"), requestMappingAttributes);
				
		List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
		annotations.add(requestMapping);

		String indextankQuerySimpleName = new JavaType("org.apache.indextank.client.indextankj.IndextankQuery").getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver());
		InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); 	
		bodyBuilder.appendFormalLine("if (q != null && q.length() != 0) {");
		bodyBuilder.indent();
		bodyBuilder.appendFormalLine(indextankQuerySimpleName + " indextankQuery = new " + indextankQuerySimpleName + "(\"" + webScaffoldAnnotationValues.getFormBackingObject().getSimpleTypeName().toLowerCase() + "_indextanksummary_t:\" + q.toLowerCase());");

		bodyBuilder.appendFormalLine("if (page != null) indextankQuery.setStart(page);");
		bodyBuilder.appendFormalLine("if (size != null) indextankQuery.setRows(size);");		
		bodyBuilder.appendFormalLine("modelMap.addAttribute(\"searchResults\", " + targetObject.getFullyQualifiedTypeName() + "." + searchAnnotationValues.getSearchMethod() + "(indextankQuery).getResults());");
		bodyBuilder.indentRemove();
		bodyBuilder.appendFormalLine("}");
		bodyBuilder.appendFormalLine("return \"" + webScaffoldAnnotationValues.getPath() + "/search\";");
		
		MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName, JavaType.STRING_OBJECT, paramTypes, paramNames, bodyBuilder);
		methodBuilder.setAnnotations(annotations);
		return methodBuilder.build();
	}
    */	
	
	private List<AnnotationMetadata> getRequestParamAnnotation(String paramName, boolean required) {
		List<AnnotationAttributeValue<?>> attributeValue = new ArrayList<AnnotationAttributeValue<?>>();
		if (!required) {
			attributeValue.add(new BooleanAttributeValue(new JavaSymbolName("required"), false));
		}
		attributeValue.add(new StringAttributeValue(new JavaSymbolName("value"), paramName));
		List<AnnotationMetadata> paramAnnotations = new ArrayList<AnnotationMetadata>();
		AnnotationMetadataBuilder annotationBuilder = new AnnotationMetadataBuilder(new JavaType("org.springframework.web.bind.annotation.RequestParam"), attributeValue);
		paramAnnotations.add(annotationBuilder.build());
		return paramAnnotations;
	}
	
	private MethodMetadata methodExists(JavaSymbolName methodName) {
		// We have no access to method parameter information, so we scan by name alone and treat any match as authoritative
		// We do not scan the superclass, as the caller is expected to know we'll only scan the current class
		for (MethodMetadata method : governorTypeDetails.getDeclaredMethods()) {
			if (method.getMethodName().equals(methodName)) {
				// Found a method of the expected name; we won't check method parameters though
				return method;
			}
		}
		return null;
	}
	
	public String toString() {
		ToStringCreator tsc = new ToStringCreator(this);
		tsc.append("identifier", getId());
		tsc.append("valid", valid);
		tsc.append("aspectName", aspectName);
		tsc.append("destinationType", destination);
		tsc.append("governor", governorPhysicalTypeMetadata.getId());
		tsc.append("itdTypeDetails", itdTypeDetails);
		return tsc.toString();
	}

	public static final String getMetadataIdentiferType() {
		return PROVIDES_TYPE;
	}
	
	public static final String createIdentifier(JavaType javaType, Path path) {
		return PhysicalTypeIdentifierNamingUtils.createIdentifier(PROVIDES_TYPE_STRING, javaType, path);
	}

	public static final JavaType getJavaType(String metadataIdentificationString) {
		return PhysicalTypeIdentifierNamingUtils.getJavaType(PROVIDES_TYPE_STRING, metadataIdentificationString);
	}

	public static final Path getPath(String metadataIdentificationString) {
		return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING, metadataIdentificationString);
	}

	public static boolean isValid(String metadataIdentificationString) {
		return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING, metadataIdentificationString);
	}
}
